package com.seafood.service.impl;

import com.seafood.dto.LocationAddressDTO;
import com.seafood.entity.LocationAddress;
import com.seafood.repository.LocationAddressRepository;
import com.seafood.service.LocationAddressService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 地址位置服务实现类
 * 
 * @author System
 * @since 2024-01-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LocationAddressServiceImpl implements LocationAddressService {

    private final LocationAddressRepository locationAddressRepository;

    @Override
    @Transactional
    public LocationAddressDTO createAddress(LocationAddressDTO addressDTO) {
        log.info("创建地址信息: {}", addressDTO.getAddressName());
        
        LocationAddress address = convertToEntity(addressDTO);
        address = locationAddressRepository.save(address);
        
        log.info("地址创建成功，ID: {}", address.getId());
        return convertToDTO(address);
    }

    @Override
    @Transactional
    public LocationAddressDTO updateAddress(Long id, LocationAddressDTO addressDTO) {
        log.info("更新地址信息，ID: {}", id);
        
        LocationAddress existingAddress = locationAddressRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("地址不存在，ID: " + id));
        
        // 更新地址信息
        existingAddress.setAddressName(addressDTO.getAddressName());
        existingAddress.setDetailedAddress(addressDTO.getDetailedAddress());
        existingAddress.setLatitude(addressDTO.getLatitude());
        existingAddress.setLongitude(addressDTO.getLongitude());
        existingAddress.setAddressType(LocationAddress.AddressType.valueOf(addressDTO.getAddressType()));
        existingAddress.setCountryCode(addressDTO.getCountryCode());
        existingAddress.setProvince(addressDTO.getProvince());
        existingAddress.setCity(addressDTO.getCity());
        existingAddress.setDistrict(addressDTO.getDistrict());
        existingAddress.setPostalCode(addressDTO.getPostalCode());
        existingAddress.setContactName(addressDTO.getContactName());
        existingAddress.setContactPhone(addressDTO.getContactPhone());
        existingAddress.setIsActive(addressDTO.getIsActive() != null ? addressDTO.getIsActive() : true);
        existingAddress.setRemarks(addressDTO.getRemarks());
        
        existingAddress = locationAddressRepository.save(existingAddress);
        
        log.info("地址更新成功，ID: {}", id);
        return convertToDTO(existingAddress);
    }

    @Override
    @Transactional(readOnly = true)
    public LocationAddressDTO getAddressById(Long id) {
        log.debug("查询地址信息，ID: {}", id);
        
        return locationAddressRepository.findById(id)
                .map(this::convertToDTO)
                .orElse(null);
    }

    @Override
    @Transactional(readOnly = true)
    public List<LocationAddressDTO> getAddressesByBusiness(Long businessId, String addressType) {
        log.debug("查询业务地址列表，业务ID: {}, 地址类型: {}", businessId, addressType);
        
        LocationAddress.AddressType type = LocationAddress.AddressType.valueOf(addressType);
        
        return locationAddressRepository.findByBusinessIdAndAddressTypeAndIsActiveTrue(businessId, type)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public List<LocationAddressDTO> getUserAddresses(Long businessId) {
        log.debug("查询用户所有地址，业务ID: {}", businessId);
        
        return locationAddressRepository.findByBusinessIdAndIsActiveTrueOrderByIsDefaultDescCreatedAtDesc(businessId)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public Page<LocationAddressDTO> getAddressesByType(String addressType, Pageable pageable) {
        log.debug("按类型分页查询地址列表，类型: {}", addressType);
        
        LocationAddress.AddressType type = LocationAddress.AddressType.valueOf(addressType);
        
        return locationAddressRepository.findByAddressTypeAndIsActiveTrue(type, pageable)
                .map(this::convertToDTO);
    }

    @Override
    @Transactional(readOnly = true)
    public Page<LocationAddressDTO> searchAddresses(String keyword, Pageable pageable) {
        log.debug("搜索地址，关键词: {}", keyword);
        
        return locationAddressRepository.searchByKeyword(keyword, pageable)
                .map(this::convertToDTO);
    }

    @Override
    @Transactional(readOnly = true)
    public LocationAddressDTO getDefaultAddress(Long businessId, String addressType) {
        log.debug("查询默认地址，业务ID: {}, 类型: {}", businessId, addressType);
        
        LocationAddress.AddressType type = LocationAddress.AddressType.valueOf(addressType);
        
        return locationAddressRepository.findByBusinessIdAndAddressTypeAndIsDefaultTrueAndIsActiveTrue(businessId, type)
                .map(this::convertToDTO)
                .orElse(null);
    }

    @Override
    @Transactional
    public LocationAddressDTO setDefaultAddress(Long addressId, Long businessId, String addressType) {
        log.info("设置默认地址，地址ID: {}, 业务ID: {}, 类型: {}", addressId, businessId, addressType);
        
        LocationAddress address = locationAddressRepository.findById(addressId)
                .orElseThrow(() -> new RuntimeException("地址不存在，ID: " + addressId));
        
        // 先取消同类型的其他默认地址
        LocationAddress.AddressType type = LocationAddress.AddressType.valueOf(addressType);
        List<LocationAddress> existingDefaults = locationAddressRepository
                .findByBusinessIdAndAddressTypeAndIsActiveTrue(businessId, type);
        
        for (LocationAddress existing : existingDefaults) {
            existing.setIsDefault(false);
            locationAddressRepository.save(existing);
        }
        
        // 设置新的默认地址
        address.setIsDefault(true);
        address = locationAddressRepository.save(address);
        
        log.info("默认地址设置成功");
        return convertToDTO(address);
    }

    @Override
    @Transactional(readOnly = true)
    public List<LocationAddressDTO> findNearbyAddresses(BigDecimal latitude, BigDecimal longitude, 
                                                       Double radiusKm, String addressType) {
        log.debug("搜索附近地址，中心点: ({}, {}), 半径: {}km, 类型: {}", 
                 latitude, longitude, radiusKm, addressType);
        
        LocationAddress.AddressType type = LocationAddress.AddressType.valueOf(addressType);
        
        // 简单实现：使用经纬度差值近似计算（1度约111km）
        BigDecimal latRange = BigDecimal.valueOf(radiusKm / 111.0);
        BigDecimal lonRange = BigDecimal.valueOf(radiusKm / (111.0 * Math.cos(Math.toRadians(latitude.doubleValue()))));
        
        return locationAddressRepository.findNearbyAddresses(latitude, longitude, latRange, lonRange, type)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public List<LocationAddressDTO> findAddressesInRange(BigDecimal minLat, BigDecimal maxLat, 
                                                        BigDecimal minLon, BigDecimal maxLon) {
        log.debug("按坐标范围查询地址，纬度: [{}, {}], 经度: [{}, {}]", 
                 minLat, maxLat, minLon, maxLon);
        
        return locationAddressRepository.findByCoordinateRange(minLat, maxLat, minLon, maxLon)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void deleteAddress(Long id) {
        log.info("删除地址，ID: {}", id);
        
        LocationAddress address = locationAddressRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("地址不存在，ID: " + id));
        
        // 软删除 - 设置为非活跃状态
        address.setIsActive(false);
        locationAddressRepository.save(address);
        
        log.info("地址删除成功，ID: {}", id);
    }

    @Override
    @Transactional
    public void deleteAddresses(List<Long> addressIds) {
        log.info("批量删除地址，数量: {}", addressIds.size());
        
        for (Long id : addressIds) {
            try {
                deleteAddress(id);
            } catch (Exception e) {
                log.warn("删除地址失败，ID: {}, 错误: {}", id, e.getMessage());
            }
        }
        
        log.info("批量删除地址完成");
    }

    @Override
    @Transactional
    public LocationAddressDTO toggleAddressStatus(Long id, Boolean isActive) {
        log.info("切换地址状态，ID: {}, 状态: {}", id, isActive);
        
        LocationAddress address = locationAddressRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("地址不存在，ID: " + id));
        
        address.setIsActive(isActive);
        address = locationAddressRepository.save(address);
        
        log.info("地址状态切换成功");
        return convertToDTO(address);
    }

    @Override
    @Transactional(readOnly = true)
    public LocationAddressDTO validateAndEnrichAddress(LocationAddressDTO addressDTO) {
        log.info("验证并丰富地址信息: {}", addressDTO.getAddressName());
        
        // 模拟实现：简单验证并返回原数据
        if (addressDTO.getLatitude() == null || addressDTO.getLongitude() == null) {
            throw new RuntimeException("经纬度信息不能为空");
        }
        
        // 设置地理编码精度（模拟）
        addressDTO.setGeocodingAccuracy(8);
        addressDTO.setGeocodingSource("模拟数据");
        
        log.info("地址验证并丰富成功");
        return addressDTO;
    }

    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getAddressStatistics() {
        log.info("获取地址统计信息");
        
        Map<String, Object> statistics = new HashMap<>();
        
        // 基本统计
        statistics.put("totalAddresses", locationAddressRepository.count());
        
        // 按类型统计
        List<Object[]> typeStats = locationAddressRepository.countByAddressType();
        Map<String, Long> typeMap = new HashMap<>();
        for (Object[] stat : typeStats) {
            typeMap.put(stat[0].toString(), (Long) stat[1]);
        }
        statistics.put("addressTypeStats", typeMap);
        
        // 按省份统计
        List<Object[]> provinceStats = locationAddressRepository.countByProvince();
        Map<String, Long> provinceMap = new HashMap<>();
        for (Object[] stat : provinceStats) {
            provinceMap.put((String) stat[0], (Long) stat[1]);
        }
        statistics.put("provinceStats", provinceMap);
        
        log.info("地址统计信息获取成功");
        return statistics;
    }

    @Override
    @Transactional
    public List<LocationAddressDTO> importAddresses(List<LocationAddressDTO> addresses) {
        log.info("导入地址数据，数量: {}", addresses.size());
        
        List<LocationAddressDTO> importedAddresses = new ArrayList<>();
        
        for (LocationAddressDTO addressDTO : addresses) {
            try {
                // 验证并丰富地址信息
                LocationAddressDTO enriched = validateAndEnrichAddress(addressDTO);
                LocationAddress address = convertToEntity(enriched);
                address = locationAddressRepository.save(address);
                importedAddresses.add(convertToDTO(address));
            } catch (Exception e) {
                log.warn("地址导入失败: {}, 错误: {}", addressDTO.getAddressName(), e.getMessage());
            }
        }
        
        log.info("地址数据导入完成，成功导入: {} 条", importedAddresses.size());
        return importedAddresses;
    }

    @Override
    @Transactional(readOnly = true)
    public List<LocationAddressDTO> exportAddresses(String addressType, String province, String city) {
        log.info("导出地址数据，类型: {}, 省份: {}, 城市: {}", addressType, province, city);
        
        List<LocationAddress> addresses = locationAddressRepository.findAll();
        
        // 按条件筛选
        if (addressType != null && !addressType.isEmpty()) {
            LocationAddress.AddressType type = LocationAddress.AddressType.valueOf(addressType);
            addresses = addresses.stream()
                    .filter(addr -> addr.getAddressType() == type)
                    .collect(Collectors.toList());
        }
        
        if (province != null && !province.isEmpty()) {
            addresses = addresses.stream()
                    .filter(addr -> province.equals(addr.getProvince()))
                    .collect(Collectors.toList());
        }
        
        if (city != null && !city.isEmpty()) {
            addresses = addresses.stream()
                    .filter(addr -> city.equals(addr.getCity()))
                    .collect(Collectors.toList());
        }
        
        List<LocationAddressDTO> result = addresses.stream()
                .filter(addr -> addr.getIsActive())
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        log.info("地址数据导出完成，数量: {}", result.size());
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public List<LocationAddressDTO> checkDuplicateAddresses(BigDecimal latitude, BigDecimal longitude, Long excludeId) {
        log.debug("检查重复地址，坐标: ({}, {}), 排除ID: {}", latitude, longitude, excludeId);
        
        List<LocationAddressDTO> duplicates = locationAddressRepository
                .findDuplicateCoordinates(latitude, longitude, excludeId != null ? excludeId : -1L)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        log.debug("找到重复地址数量: {}", duplicates.size());
        return duplicates;
    }

    /**
     * 将DTO转换为实体
     */
    private LocationAddress convertToEntity(LocationAddressDTO dto) {
        LocationAddress address = new LocationAddress();
        
        address.setBusinessId(dto.getBusinessId());
        address.setAddressName(dto.getAddressName());
        address.setDetailedAddress(dto.getDetailedAddress());
        address.setLatitude(dto.getLatitude());
        address.setLongitude(dto.getLongitude());
        address.setAddressType(LocationAddress.AddressType.valueOf(dto.getAddressType()));
        address.setCountryCode(dto.getCountryCode());
        address.setProvince(dto.getProvince());
        address.setCity(dto.getCity());
        address.setDistrict(dto.getDistrict());
        address.setPostalCode(dto.getPostalCode());
        address.setContactName(dto.getContactName());
        address.setContactPhone(dto.getContactPhone());
        address.setIsActive(dto.getIsActive() != null ? dto.getIsActive() : true);
        address.setIsDefault(dto.getIsDefault() != null ? dto.getIsDefault() : false);
        address.setAddressTag(dto.getAddressTag());
        address.setRemarks(dto.getRemarks());
        address.setFormattedAddress(dto.getFormattedAddress());
        address.setGeocodingAccuracy(dto.getGeocodingAccuracy());
        address.setGeocodingSource(dto.getGeocodingSource());
        
        return address;
    }

    /**
     * 将实体转换为DTO
     */
    private LocationAddressDTO convertToDTO(LocationAddress address) {
        LocationAddressDTO dto = new LocationAddressDTO();
        
        dto.setId(address.getId());
        dto.setBusinessId(address.getBusinessId());
        dto.setAddressName(address.getAddressName());
        dto.setDetailedAddress(address.getDetailedAddress());
        dto.setLatitude(address.getLatitude());
        dto.setLongitude(address.getLongitude());
        dto.setAddressType(address.getAddressType().name());
        dto.setCountryCode(address.getCountryCode());
        dto.setProvince(address.getProvince());
        dto.setCity(address.getCity());
        dto.setDistrict(address.getDistrict());
        dto.setPostalCode(address.getPostalCode());
        dto.setContactName(address.getContactName());
        dto.setContactPhone(address.getContactPhone());
        dto.setIsActive(address.getIsActive());
        dto.setIsDefault(address.getIsDefault());
        dto.setAddressTag(address.getAddressTag());
        dto.setRemarks(address.getRemarks());
        dto.setFormattedAddress(address.getFormattedAddress());
        dto.setGeocodingAccuracy(address.getGeocodingAccuracy());
        dto.setGeocodingSource(address.getGeocodingSource());
        dto.setCreatedAt(address.getCreatedAt() != null ? address.getCreatedAt().toString() : null);
        dto.setUpdatedAt(address.getUpdatedAt() != null ? address.getUpdatedAt().toString() : null);
        
        return dto;
    }
}